/*
 * Copyright (C) 2008 Universidade Federal de Campina Grande
 *  
 * This file is part of OurGrid. 
 *
 * OurGrid is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free 
 * Software Foundation, either version 3 of the License, or (at your option) 
 * any later version. 
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public License 
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package org.ourgrid.peer.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;

import org.ourgrid.common.interfaces.LocalWorkerProviderClient;
import org.ourgrid.common.interfaces.to.RequestSpec;
import org.ourgrid.common.statistics.control.JobControl;
import org.ourgrid.common.statistics.control.LoginControl;
import org.ourgrid.common.statistics.control.UserControl;
import org.ourgrid.peer.controller.accounting.AccountingCommitController;
import org.ourgrid.peer.controller.allocation.RedistributionController;
import org.ourgrid.peer.controller.messages.ConsumerMessages;
import org.ourgrid.peer.dao.ConsumerDAO;
import org.ourgrid.peer.dao.RequestDAO;
import org.ourgrid.peer.dao.UsersDAO;
import org.ourgrid.peer.to.AllocableWorker;
import org.ourgrid.peer.to.PeerUser;
import org.ourgrid.peer.to.Request;
import org.ourgrid.reqtrace.Req;

import br.edu.ufcg.lsd.commune.api.FailureNotification;
import br.edu.ufcg.lsd.commune.api.InvokeOnDeploy;
import br.edu.ufcg.lsd.commune.api.RecoveryNotification;
import br.edu.ufcg.lsd.commune.container.servicemanager.ServiceManager;
import br.edu.ufcg.lsd.commune.identification.ContainerID;
import br.edu.ufcg.lsd.commune.identification.DeploymentID;

/**
 * Implement Peer actions when a consumer recover or fail.
 */
public class WorkerProviderClientFailureController {

	private ServiceManager serviceManager;

	@InvokeOnDeploy
	public void init(ServiceManager serviceManager) {
		this.serviceManager = serviceManager;
	}

	@RecoveryNotification
	public void doNotifyRecovery(LocalWorkerProviderClient monitorable, DeploymentID monitorableID) {}

	
	@Req("REQ022")
	@FailureNotification
	public void doNotifyFailure(LocalWorkerProviderClient monitorable, DeploymentID monitorableID) {

		UsersDAO usersDAO = serviceManager.getDAO(UsersDAO.class);
		String monitorablePubKey = monitorableID.getPublicKey();

		ContainerID accessPointID = monitorableID.getServiceID().getContainerID();
		String login = accessPointID.getUserName() + "@" + accessPointID.getServerName();
		
		PeerUser user = UserControl.getInstance().getUser(login, usersDAO);
		
		if(user == null) {
			serviceManager.getLog().debug(ConsumerMessages.getUnknownLocalConsumerFailureMessage(monitorableID));
			return;
		}
		
		if(!UserControl.getInstance().userExists(monitorablePubKey, usersDAO)) {
			serviceManager.getLog().debug(ConsumerMessages.getWrongPubKeyLocalConsumerFailureMessage(monitorableID, monitorablePubKey));
			return;
		}
	
		if(!usersDAO.isLoggedUser(monitorablePubKey)) {
			serviceManager.getLog().debug(ConsumerMessages.getOfflineLocalConsumerFailureMessage(monitorableID));
			return;
		}
		
		serviceManager.getLog().info(ConsumerMessages.getLocalConsumerFailureMessage(monitorableID));
		LoginControl.getInstance().localConsumerFailure(user, serviceManager);
		
		finishUserRequests(serviceManager, monitorablePubKey);
		
		usersDAO.removeLoggedUser(monitorablePubKey);
		serviceManager.getDAO(ConsumerDAO.class).removeLocalConsumer(monitorablePubKey);
		
		serviceManager.release(monitorable);
	}

	public static void finishUserRequests(ServiceManager serviceManager, String monitorablePubKey) {
		RequestDAO requestDAO = serviceManager.getDAO(RequestDAO.class);
		
		for(Request request : requestDAO.getRunningRequests()) {
			
			if(request.getConsumer().getPublicKey().equals(monitorablePubKey)) {

				RequestSpec requestSpec = request.getSpec();
				requestDAO.removeRequest(requestSpec.getRequestId());
				AccountingCommitController.getInstance().commitAccounting(requestSpec, serviceManager);
				
				Future<?> future = requestDAO.removeFuture(requestSpec);
				if(future != null) {
					future.cancel(true);
				}
				
				List<AllocableWorker> allocs = new ArrayList<AllocableWorker>(request.getAllocableWorkers()); 
				for(AllocableWorker alloc : allocs) {
					RedistributionController.getInstance().redistributeWorker(alloc, serviceManager);
				}
			}
			
			JobControl.getInstance().finishRequest(request, true, serviceManager);
		}
	}
}